package util

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)


const (
	CLIPARSER_ERROR_CMD_NOT_EXISTS string = "cmd_not_exist"
	CLIPARSER_ERROR_CMD_ERR_CONFIG string = "cmd_err_config"
	CLIPARSER_ERROR_CMD_NO_HANDLE = "cmd_no_handle"
)

/// 解析配置
type CliParserConfig struct {
	//是否解析命令, 开启表示支持命令模式(支持command参数)
	MultiCommand bool
	//无arg模式(此程序不需要任务参数)
	NoArgument bool
	//帮助文档翻译配置
	Translate CliParserTranslate
	//命令模式下, 默认命令
	DefaultCommand string
	//非命令模式下的执行
	DefultHandle CliParserCommand
	//支持的命令配置
	Commands []CliParserCommand
	//错误回调
	ErrorHandle func(CliParserErrorEvent)
	//不检测不支持的选项和开关
	AllowUnsupportParam bool
}

// 增加命令
func (cc *CliParserConfig) AddCommand(ccs *CliParserCommand) *CliParserConfig {
	cc.Commands = append(cc.Commands, *ccs)
	return cc
}

//默认命令
func (this *CliParserConfig) SetDefaultHandle(ccs *CliParserCommand) *CliParserConfig {
	this.DefultHandle = *ccs
	return this
}

// 解析结果
type CliParser struct {
	commandName    	 	string
	args  	 	[]string
	options    	map[string]string
	switches 	map[string]bool
	config 	CliParserConfig
}

func (this *CliParser) SetConfig(conf *CliParserConfig) *CliParser {
	this.config = *conf
	return this
}

func (this *CliParser) GetComnandName() string {
	return this.commandName
}

func (this *CliParser) GetArguments() []string {
	return this.args
}

func (this *CliParser) GetArgument(index int, def ...string) string {
	if len(this.args) <= index {
		return GetFuncDefaultArg(def, "")
	}
	return this.args[index]
}

func (this *CliParser) HasOption(name string) bool {
	_, ok := this.options[name]
	return ok
}

func (this *CliParser) getOption(name string, def ...string) string {
	val, ok := this.options[name]

	if !ok || val == "" {
		return GetFuncDefaultArg(def, "")
	}

	return val
}

func (this *CliParser) switchOn(name string) bool {
	_, ok := this.switches[name]
	return ok
}

/// 解析命令行参数
func (this *CliParser) Parse() *CliParser {
	args := os.Args[1:]
	argn := len(args)
	this.args = make([]string, 0)
	this.options = make(map[string]string)
	this.switches = make(map[string]bool)

	for i:=0; i<argn; i++ {
		arg := args[i]
		arglen := len(arg)

		if arglen == 0 {
			continue
		}

		argBytes := []byte(arg)

		if argBytes[0] == '-' {
			if arglen > 2 && argBytes[1] == '-' {
				//option
				key := arg[2:]
				eqIndex := strings.Index(key, "=")
				
				if eqIndex == -1 {
					if argn > i+1 {
						this.options[key] = args[i+1]
						i++
					} else {
						this.options[key] = ""
					}
				} else {
					val := key[eqIndex+1:]
					key = key[0:eqIndex]
					this.options[key] = val
				}
				continue
			} else if arglen == 2 && argBytes[1] == '-' {
				//args
				this.args = append(this.args, args[i:]...)
				break
			} else {
				//switches
				ary := strings.Split(arg[1:], "")
				for _,val := range ary {
					this.switches[val] = true
				}
			}
		} else if this.config.MultiCommand && this.commandName == "" {
			//cmd
			this.commandName = arg
		} else {
			//arg
			this.args = append(this.args, arg)
		}
	}

	return this
}

//响应命令的回调
func (this *CliParser) Handle() {
	cmdstr := Triop(this.GetComnandName() != "", this.GetComnandName(), this.config.DefaultCommand)
	conf   := this.config
	trans  := conf.Translate

	handle_error := func(typ string, msg string) {
		if conf.ErrorHandle != nil {
			conf.ErrorHandle(CliParserErrorEvent{
				Typ: typ,
				Msg: msg,
				CommandName: this.GetComnandName(),
			})
		}
	}

	handle_command := func (cpc *CliParserCommand) {
		if cpc.Handle == nil {
			handle_error(CLIPARSER_ERROR_CMD_NO_HANDLE, "命令缺少Handle配置")
			PrintErrorf("缺少Handle配置")
			return
		}

		cpc.Handle(CliParserCommandHandleEvent{
			Command: *cpc,
			HandleError: handle_error,
		})
	}

	helpIndent := "  "

	print_command_help := func (cpc *CliParserCommand) {
		fmt.Printf("%s\n\n", cpc.Help)

		fmt.Printf("%s:\n", DefaultStr(trans.UsageSectionLabel, "用法"))
		fmt.Printf("%s%s%s%s%s%s",
			helpIndent, 
			Triop(trans.BinName=="", "xxx", trans.BinName),
			Triop(cpc.Key == "", "", fmt.Sprintf(" %s", cpc.Key)),
			Triop(cpc.ArgumentHelp != "", " [arg]", ""),
			Triop(len(cpc.Switchs) > 0, " [-switch]", ""),
			Triop(len(cpc.Options) > 0, " [--option=optionValue]", ""),
		)

		if cpc.ArgumentHelp != "" {
			fmt.Printf("\n\n%s:", DefaultStr(trans.ArgumentSectionLabel, "参数说明(arg)"))
			fmt.Printf("\n%s%s", helpIndent, cpc.ArgumentHelp)
		}

		if len(cpc.Switchs) > 0 {
			fmt.Printf("\n\n%s:", DefaultStr(trans.SwitchSectionLabel, "支持开关(switch)"))
			//计算命令的最大长度
			for _, swName := range cpc.Switchs {
				fmt.Printf("\n%s%s %s", helpIndent, swName.Key, swName.Help)
			}
		}
	
		if len(cpc.Options) > 0 {
			//计算选项名称的最大长度
			maxOpNameLen := ArrayReduce(cpc.Options, 0, func(ret int, row CliParserOption, i int) int {
				return max(ret, len(row.Key))
			})

			fmt.Printf("\n\n%s:", DefaultStr(trans.SwitchSectionLabel, "支持选项(option)"))
			//计算命令的最大长度
			for _, opName := range cpc.Options {
				fmt.Printf("\n%s%"+ strconv.Itoa(maxOpNameLen) +"s %s", helpIndent, opName.Key, opName.Help)
			}
		}

		fmt.Print("\n\n")
	}

	print_multi_command_global_help := func () {
		fmt.Printf(
			"%s%s%s\n\n", 
			trans.MultiCommandHelp,
			Triop(trans.Version=="", "", "\n版本号:"),
			trans.Version,
		)

		//计算总的options数量和switch数量
		totalCounts := ArrayMap(this.config.Commands, func (row CliParserCommand, i int) []int {
			return []int {len(row.Options), len(row.Switchs)}
		})

		totalCountAry := []int{0,0}
		ArrayReduce(totalCounts, &totalCountAry, 
			func (ret *[]int, row []int, i int) *[]int  {
				(*ret)[0] +=row[0]
				(*ret)[1] +=row[1]
				return ret
			},
		)
	
		globalOptionCount := totalCountAry[0]
		globalSwitchCount := totalCountAry[1]

		fmt.Printf("%s:\n", DefaultStr(trans.UsageSectionLabel, "用法"))
		fmt.Printf("%s%s%s%s%s%s",
			helpIndent, 
			Triop(trans.BinName=="", "程序路径", trans.BinName),
			Triop(conf.MultiCommand, " [command]","",),
			Triop(!conf.NoArgument, " [arg]", ""),
			Triop(globalSwitchCount > 0, " [-switch]", ""),
			Triop(globalOptionCount > 0, " [--option=optionValue]", ""),
		)

		//计算命令名称的最大长度
		maxNameLen := ArrayReduce(conf.Commands, 0, func(ret int, row CliParserCommand, i int) int {
			return max(ret, len(row.Key))
		})

		//打印命令列表
		fmt.Printf("\n\n%s:", DefaultStr(trans.CommandSectionLabel, "支持命令(command)"))
		for _, cmdRow := range conf.Commands {
			if cmdRow.Key == "" {
				continue
			}

			fmt.Printf("\n%s%"+ strconv.Itoa(maxNameLen) +"s %s", helpIndent, cmdRow.Key, cmdRow.Desc)
		}

		fmt.Printf("\n\n可以使用命令 %s [command] --help 查看对应命令的帮助文档\n", trans.BinName)
		fmt.Print("\n")
	}

	var command *CliParserCommand
	if conf.MultiCommand {
		command = find_command(&this.config.Commands, cmdstr)

		if command == nil && conf.DefaultCommand != "" {
			cmdstr = conf.DefaultCommand
			command = find_command(&this.config.Commands, cmdstr)
		}

		if command == nil {
			if cmdstr == "" {
				print_multi_command_global_help()
				return
			}

			handle_error(CLIPARSER_ERROR_CMD_NOT_EXISTS, "命令不存在")
			PrintErrorf("命令不存在: %s", cmdstr)
			return
		}
	} else {
		command = &conf.DefultHandle
	}

	if !conf.AllowUnsupportParam {
		//检测选项和开关是否支持
	}

 	if this.HasOption("help") {
		if conf.MultiCommand && this.GetComnandName() == "" {
			print_multi_command_global_help()
		} else {
			print_command_help(command)
		}
	} else {
		handle_command(command)
	}
}

type CliParserErrorEvent struct {
	Typ string
	Msg string
	CommandName string
}

/// 查找命令
func find_command(ary *[]CliParserCommand, cmdName string) *CliParserCommand {
	for _, item := range *ary {
		if item.Key == cmdName {
			return &item
		}
	}

	return nil
}

/// 帮助文档翻译配置
type CliParserTranslate struct {
	//二进制文件名名称
	BinName string
	//版本号
	Version string
	//命令行工具描述(多命令默认help说明)
	MultiCommandHelp string
	//使用方法标签文本
	UsageSectionLabel string
	//命令列表标签文本
	CommandSectionLabel string
	//开关标签文本
	SwitchSectionLabel string
	//选项标签文本
	OptionSectionLabel string
	//数值说明标签文本
	ArgumentSectionLabel string
}

/// 命令定义配置
type CliParserCommand struct {
	Key string
	//帮助文档
	Help string
	//数值文档
	ArgumentHelp string
	//简要描述
	Desc string
	//命令匹配的回调函数
	Handle func(CliParserCommandHandleEvent)
	//开关
	Switchs []CliParserSwitch
	//选项
	Options []CliParserOption
}

func (cpc *CliParserCommand) AddSwitch(key string, help ...string) *CliParserCommand {
	if key == "" {
		return cpc
	}

	cpc.Switchs = append(cpc.Switchs, CliParserSwitch{Key: key[0:1], Help: GetFuncDefaultArg(help, "")})
	return cpc
}

//添加命令选项
func (cpc *CliParserCommand) AddOption(key string, help string) *CliParserCommand {
	return cpc.AddOptionWithVerify(key, help, nil)
}

//添加命令选项(支持验证回调)
func (cpc *CliParserCommand) AddOptionWithVerify(key string, help string, c func(*CliParserVerifyCallbackEvent)) *CliParserCommand {
	if key == "" {
		return cpc
	}

	cpc.Options = append(cpc.Options, CliParserOption{Key: key, Help: help, VerifyCallback: c})
	return cpc
}

//开关定义
type CliParserSwitch struct {
	Key string
	Help string
}

type CliParserOption struct {
	//命令key
	Key string
	//帮助文档
	Help string
	//数值检测回调
	VerifyCallback func(*CliParserVerifyCallbackEvent)
}

type CliParserVerifyCallbackEvent struct {

}

type CliParserCommandHandleEvent struct {
	Command CliParserCommand
	HandleError func(string, string)
}

type CliParserDefaultHandleEvent struct {

}